React - fetch gebruiken in een bestaande component
Home

React - fetch gebruiken in een bestaande component

React - fetch gebruiken in een bestaande component

Als je nieuwe functionaliteit aan een bestaande component wilt toevoegen, denk dan in 'knopen' die herbruikbaar zijn. De nieuwe functionaliteit moet zo worden toegevoegd dat vroegere code nog altijd werkt.

Probleem

We hebben een LikePanel klasse component gemaakt. Telkens de gebruiker op de knop klikt wordt de teller, die de likes bijhoudt, met 1 vermeerderd en wordt dit resultaat in de browser getoond.

Nu willen we twee mogelijkheden toevoegen:

  1. het aantal likes uit een tabel in de database ophalen;
  2. als er op de Like-knop in de LikePanel geklikt wordt moet de nieuwe waarde van het aantal likes in de tabel in de database opgeslagen worden;

Wijzigingen MmtApi

  1. Wanneer er voor de key nog geen rij in de tabel bestaat, retourneer een MmtLike instantie met Likes ingesteld op 0.
    1. Daarvoor voegen we eerst een constructor overload toe aan de MmtLike modelklasse:
      public MmtLike(string key)
      {
          this.Id = 0;
          this.Key = key;
          this.Name = "niet van toepassing";
          this.Likes = 0;
      }
    2. In de MmtLineController moet de get methode een instantie van de modelklasse retourneren met Likes ingesteld op 0 als er nog geen rij voor dit artikel in de Likes tabel aanwezig is. Nu retourneert die methode een null waarde indien dit het geval is, en dat genereert een fout in de client code. Er wordt slechts een rij in de tabel Likes gemaakt wanneer een post wordt uitgevoerd met een key-waarde die nog niet bestaat.
      We gebruiken hiervoor de constructor die we net hebben toegevoegd aan de modelklasse:
      [HttpGet("{key}")]
      public MmtLike GetLikes(string key)
      {
          var mmtLike = mmtContext.MmtLikes.Where(a => a.Key == key).FirstOrDefault();
      
          mmtLike = mmtLike == null ? new MmtLike(key) : mmtLike;
          return mmtLike;
      
      }
    3. Vergeet niet een lege constructor toe te voegen als je nog van de default constructor gebruik wilt maken:
      public MmtLike() { }
    4. In de MmtLineController retourneert de post methode een instantie van de modelklasse ingesteld op null als er nog geen rij voor dit artikel in de Likes tabel aanwezig is. De post methode maakt echter een rij in de tabel MmtLikes en moet dus die waarde retourneren:
      [HttpPost]
      public MmtLike PostLike(MmtLike item)
      {
          var mmtLike = mmtContext.MmtLikes.Where(a => a.Key == item.Key).FirstOrDefault();
      
          if (mmtLike == null)
          {
              mmtContext.MmtLikes.Add(item);
              mmtContext.SaveChanges();
              mmtLike = item;
          }
          else
          {
              mmtLike.Likes = mmtLike.Likes + 1;
              mmtContext.MmtLikes.Update(mmtLike);
              mmtContext.SaveChanges();
          }
          return mmtLike;
      }
      

Wijzigingen mmt-react client

We voegen een keyValue attribuut in de JSX tag van <LikePanel />.

  1. Op basis daarvan gaat de LikePanel component beslissen of data uit de databank gehaald moet worden of niet. Op die manier zal de LikePanel, die in oudere code al werd gebruikt, nog steeds werken. In de ArticleDetail component, die staat in het in ui-article.cs bestand voegen we een attribuut toe aan de <LikePanel/> tag. We geven de key eigenschap van het artikel mee dat we in detail willen tonen. Het is de waarde die we gebruiken om de rij in de MmtLikes tabel op te zoeken:
    <LikePanel keyValue={this.props.article.key}/>
    
  2. We declareren bovenaan een eigenschap waarin we de url van de MmtApi opslaan:
    class LikePanel extends React.Component {
        host = "http://localhost:58013/MmtLike";
  3. We voegen de methode getLikes toe aan de LikePanel componentklasse. De get wordt alleen uitgevoerd als het keyValue attribuut gedefinieerd is. Op de manier zal de legacy code nog blijven werken:
    getLikes = () => {
        const keyValue = this.props.keyValue;
        let count = 0;
        if (typeof keyValue !== 'undefined') {
            // als de callback wordt uitgevoerd, is this niet meer in de scope
            // daarom slaan we die op in een constante en geven die met de callback mee 
            const self = this;
            const url = `${this.host}/${keyValue}`;
            fetch(url)
                .then(response => response.json())
                .then(data => {
                    this.setState({
                        likes: data.likes
                    });});
        }
    }
  4. En we roepen die op in de constructor van de LikePanel componentklasse:
    constructor(props) {
        super(props); // Now 'this' is initialized by calling the parent constructor.
        this.state = {
            likes: 0
        };
        this.getLikes();
    }
  5. We voegen een methode toe met de naam postLikes
    1. We beginnen met het helpers.js bestand te laden, want daarin staat de postData methode:
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
          <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
          <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
          <script src="js/helpers.js"></script>
          <script type="text/babel" src="js/ui-controls.js"></script>
    2. We voegen de methode postLikes toe in de LikePanel componentklasse:
      postLikes = () => {
          let item = {
              Key: this.props.keyValue,
              Name: 'onbelangrijk',
              Likes: 1
          };
      
          postData(this.host, item)
              .then(data => {
                  this.setState({
                      likes: data.likes
                  });
              });
      }
    3. We roepen de methode postlikes op in de incrementLike methode, alleen als het keyValue attribuut gedefinieerd is, posten we naar de database. Op die manier houden we rekening met legacy code:
      incrementLike = () => {
          const keyValue = this.props.keyValue;
          if (typeof keyValue !== 'undefined') {
              this.postLikes();
          } else {
              let newCount = this.state.likes + 1;
              this.setState({
                  likes: newCount
              });
          }
      };
      
      Als ondertussen de Likes kolom in de MmtLikes tabel door andere gebruiker al werd geüpdated, zal deze nieuwe waarde hier ook getoond worden.

Uitproberen

  1. Run de API in Visual Studio (F5). In het bestand launchSettings.json vind je het poortnummer dat door Visual Studio werd ingeteld:
    {
      "$schema": "http://json.schemastore.org/launchsettings.json",
      "iisSettings": {
        "windowsAuthentication": false,
        "anonymousAuthentication": true,
        "iisExpress": {
          "applicationUrl": "http://localhost:58013",
          "sslPort": 0
        }
  2. Noteer de poort en kopieer die in de url in de LikePanel componentklasse:
    class LikePanel extends React.Component {
        host = "http://localhost:58013/MmtLike";
  3. Als je met een lokale MySql server werkt, moet die opgestart zijn. De remote MySQL server runt altijd.
  4. Start de mmt-react app in de terminal Visual Code met:
    php -S localhost:63344
    

    Gebruik een vrije poort naar keuze.

  5. Open de mmt-react app in de browser door de volgende url in te typen:
    http://localhost:63344/
  6. De code staat in de mmt-react map op Bitbucket.

JI
2020-05-18 15:49:07